package thesis.filesharing.dao.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;

import thesis.filesharing.common.exception.system.AppSystemException;
import thesis.filesharing.dao.GroupDAO;
import thesis.filesharing.dao.util.EMF;
import thesis.filesharing.model.Clazz;
import thesis.filesharing.model.ExerciseFile;
import thesis.filesharing.model.Group;
import thesis.filesharing.model.Student;

import com.google.appengine.api.datastore.Key;

@Repository
public class GroupDAOImpl implements GroupDAO {

	@Override
	public Group findGroupStudentRegisteredInClass(Key studentKey, Key classKey) {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			Student s = em.find(Student.class, studentKey);
			if (s != null) {
				Set<Key> groupKeys = s.getGroupKeys();
				if (groupKeys != null && !groupKeys.isEmpty()) {
					Group g = null;
					for (Key groupKey : groupKeys) {
						g = em.find(Group.class, groupKey);
						if (g != null && g.getClazz() != null && g.getClazz().getClassKey().equals(classKey)) {
							return g;
						}
					}
				}
			}
			return null;
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Group saveGroup(Group group, List<String> studentIds, Key classKey) {
		EntityManager em = null;
		EntityTransaction transaction = null;
		
		try {
			em = EMF.get().createEntityManager();
			transaction = em.getTransaction();
			transaction.begin();
			
			String jpaql = "SELECT s FROM Student s WHERE s.studentId IN (:studentIds)";
			Query query = em.createQuery(jpaql);
			query.setParameter("studentIds", studentIds);
			List<Student> students = (List<Student>) query.getResultList();
			
			if (students != null && !students.isEmpty()) {
				
				// Find class which group belongs to
				Clazz clazz = em.find(Clazz.class, classKey);
				if (clazz != null) {
					clazz.getGroups().add(group);
					em.merge(clazz);
					em.flush();
					transaction.commit();
					transaction.begin();
				}
				
				// Add group leader
				Student groupLeader = students.get(0);
				group.setGroupLeader(groupLeader);
				groupLeader.getLeadGroups().add(group);
				em.merge(groupLeader);
				
				// Add members
				for (Student s : students) {
					transaction.commit();
					transaction.begin();
					group.getGroupMembers().add(s.getStudentKey());
					s.getGroupKeys().add(group.getGroupKey());
				}
			}
			
			transaction.commit();
			return group;
		} catch (Exception ex) {
			if (transaction != null && transaction.isActive()) {
				transaction.rollback();
			}
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@Override
	public boolean checkStudentRegisteredInOtherGroup(Key studentKey, Key classKey, Key currentGroupKey) {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			Student s = em.find(Student.class, studentKey);
			if (s != null) {
				Set<Key> groupKeys = s.getGroupKeys();
				if (groupKeys != null && !groupKeys.isEmpty()) {
					Group g = null;
					for (Key groupKey : groupKeys) {
						g = em.find(Group.class, groupKey);
						if (!groupKey.equals(currentGroupKey) && g != null && g.getClazz() != null && g.getClazz().getClassKey().equals(classKey)) {
							return true;
						}
					}
				}
			}
			return false;
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public Group updateGroup(Group group, List<String> studentIds) {
		EntityManager em = null;
		EntityTransaction transaction = null;
		try {
			em = EMF.get().createEntityManager();
			transaction = em.getTransaction();
			transaction.begin();
			
			Group g = em.find(Group.class, group.getGroupKey());
			if (g != null) {
				g.setGroupName(group.getGroupName());
				g.setTopicName(group.getTopicName());
				
				String jpaql = "SELECT s FROM Student s WHERE s.studentId IN (:studentIds)";
				Query query = em.createQuery(jpaql);
				query.setParameter("studentIds", studentIds);
				List<Student> students = (List<Student>) query.getResultList();
				if (students != null && !students.isEmpty()) {
					// Delete group from all old members
					for (Key k : g.getGroupMembers()) {
						Student student = em.find(Student.class, k);
						student.getGroupKeys().remove(g.getGroupKey());
					}
					// Add group to all new members
					for (Student s : students) {
						s.getGroupKeys().add(g.getGroupKey());
					}
					// Delete all old memberKeys from group
					g.getGroupMembers().removeAll(g.getGroupMembers());
					// Add all new memberKeys to group
					for (Student s : students) {
						g.getGroupMembers().add(s.getStudentKey());
					}
					// Remove group from old leader
					Student oldLeader = g.getGroupLeader();
					List<Group> leadGroups = new ArrayList<Group>(oldLeader.getLeadGroups());
					leadGroups.remove(g);
					oldLeader.getLeadGroups().clear();
					oldLeader.getLeadGroups().addAll(leadGroups);
					// Set new groupLeader to group
					Student leader = students.get(0);
					g.setGroupLeader(leader);
					// Add more group to leadGroups collection of new Student
					leader.getLeadGroups().add(g);
				}
			}
			em.merge(g);
			em.flush();
			transaction.commit();
			return g;
		} catch (Exception ex) {
			if (transaction != null && transaction.isActive()) {
				transaction.rollback();
			}
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Group> listGroupsByClass(Key classKey) {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			Clazz clazz = em.find(Clazz.class, classKey);
			if (clazz != null) {
				String jpaql = "SELECT g FROM Group g WHERE g.clazz = :clazz ORDER BY g.groupKey ASC";
				Query query = em.createQuery(jpaql);
				query.setParameter("clazz", clazz);
				return (List<Group>) query.getResultList();
			}
			return null;
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@Override
	public void deleteGroup(Key groupKey) {
		EntityManager em = null;
		EntityTransaction transaction = null;
		try {
			em = EMF.get().createEntityManager();
			transaction = em.getTransaction();
			transaction.begin();
			
			Group group = em.find(Group.class, groupKey);
			if (group != null) {
				Set<Key> studentKeys = group.getGroupMembers();
				for (Key studentKey : studentKeys) {
					Student student = em.find(Student.class, studentKey);
					student.getGroupKeys().remove(group.getGroupKey());
				}
				Student leader = group.getGroupLeader();
				List<Group> leadGroups = new ArrayList<Group>(leader.getLeadGroups());
				leadGroups.remove(group);
				leader.getLeadGroups().clear();
				leader.getLeadGroups().addAll(leadGroups);
				
				Set<ExerciseFile> exerciseFiles = group.getExerciseFiles();
				for (ExerciseFile f : exerciseFiles) {
					ExerciseFile e = em.find(ExerciseFile.class, f.getExerciseFileKey());
					if (e != null) {
						em.remove(f);
					}
				}
			}
			group.getClazz().getGroups().remove(group);
			
			transaction.commit();
		} catch (Exception ex) {
			if (transaction != null && transaction.isActive()) {
				transaction.rollback();
			}
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}
}
